package com.varian.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.varian.mybatis.base.BaseServiceImpl;
import com.varian.system.constant.SystemConstant;
import com.varian.system.convert.SystemConvert;
import com.varian.system.dto.SysDeptDTO;
import com.varian.system.dto.SysDeptListDTO;
import com.varian.system.dto.SysDeptTreeDTO;
import com.varian.system.dto.SysDeptTreeItemDTO;
import com.varian.system.entity.SysDept;
import com.varian.system.mapper.SysDeptMapper;
import com.varian.system.service.ISysDeptService;
import com.varian.system.vo.SysDeptDiagramVO;
import com.varian.system.vo.SysDeptTreeVO;
import com.varian.system.vo.SysDeptVO;
import com.varian.task.util.TaskUtil;
import com.varian.tool.Func;
import com.varian.tool.exception.BizException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;

/**
 * @author ben
 * @since 2024/11/19
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class SysDeptServiceImpl extends BaseServiceImpl<SysDeptMapper, SysDept> implements ISysDeptService {

    private final SystemConvert baseConvert;

    @Override
    public boolean add(SysDeptDTO dto) {
        SysDept dept = baseConvert.toEntity(dto);
        validateUniqueness(dept);
        if (dept.getParentId() != null) {
            SysDept parentDept = baseMapper.selectById(dept.getParentId());
            Assert.notNull(parentDept, () -> BizException.of(SystemConstant.PARENT_DEPT_NOT_EXIST));
            dept.setDeptLevel(parentDept.getDeptLevel() + 1);
        }
        boolean result = save(dept);
        // 创建任务 新增部门
        if (result) {
            String taskName = StrUtil.format("部门新增事件:{}", dept.getDeptName());
            TaskUtil.createTask(SystemConstant.TASK_DEPT_ADD, taskName, 1, dept.getId());
        }
        return result;
    }

    @Override
    public List<SysDeptTreeVO> tree(SysDeptTreeDTO dto) {
        return Func.treeBuild(CollUtil.map(list(lambdaQuery().getWrapper()
                .eq(dto.getParentId() != null, SysDept::getParentId, dto.getParentId())
                .eq(dto.getDeptStatus() != null, SysDept::getDeptStatus, dto.getDeptStatus())
                .orderByAsc(SysDept::getDeptWeight)
                .orderByDesc(SysDept::getId)
        ), baseConvert::toTreeVO, true));
    }

    @Override
    public SysDeptDiagramVO diagram() {
        List<SysDept> deptList = list(lambdaQuery().getWrapper().orderByAsc(SysDept::getLeftValue));
        List<SysDeptDiagramVO.Edge> edges = ListUtil.list(false);
        List<SysDeptDiagramVO.Node> nodes = ListUtil.list(false);
        for (SysDept dept : deptList) {
            if (!Func.TREE_ROOT_ID.equals(dept.getParentId())) {
                edges.add(new SysDeptDiagramVO.Edge(dept.getParentId(), dept.getId()));
            }
            nodes.add(new SysDeptDiagramVO.Node(dept.getId(), dept.getDeptName(), dept.getDeptCode(), dept.getDeptLevel(), dept.getRightValue(), dept.getLeftValue()));
        }
        SysDeptDiagramVO vo = new SysDeptDiagramVO();
        vo.setEdges(edges);
        vo.setNodes(nodes);
        return vo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean fixValue() {
        // 根据部门权重正序查询
        List<SysDept> list = list(lambdaQuery().getWrapper().orderByAsc(SysDept::getDeptWeight));
        // 如果部门为空，则直接返回true
        if (CollUtil.isEmpty(list)) {
            return true;
        }
        // 修复部门左右值的错误问题
        List<SysDeptTreeItemDTO> tree = Func.treeBuild(CollUtil.map(list, baseConvert::toTreeDTO, true));
        int maxValue = 1;
        for (SysDeptTreeItemDTO dto : tree) {
            processValueAndLevel(dto, maxValue, 1);
            maxValue = dto.getRightValue() + 1;
        }
        // 更新部门权重和级别,将树形结构平铺成列表
        CollUtil.map(Func.treeFlat(tree), baseMapper::updateValueAndLevelByTree, true);
        return true;
    }

    private void processValueAndLevel(SysDeptTreeItemDTO dto, Integer value, Integer level) {
        if (dto != null) {
            dto.setDeptLevel(level);
            dto.setLeftValue(value);
            if (CollUtil.isNotEmpty(dto.getChildren())) {
                Integer maxValue = value;
                for (SysDeptTreeItemDTO child : dto.getChildren()) {
                    processValueAndLevel(child, maxValue + 1, level + 1);
                    if (child.getRightValue() > maxValue) {
                        maxValue = child.getRightValue();
                    }
                }
                dto.setRightValue(maxValue + 1);
            } else {
                dto.setRightValue(value + 1);
            }
        }
    }

    @Override
    public SysDeptVO detail(Long id) {
        SysDept dept = validateExistence(id);
        return baseConvert.toVO(dept);
    }

    @Override
    public boolean modify(Long id, SysDeptDTO dto) {
        Assert.isFalse(id.equals(dto.getParentId()), () -> BizException.of(SystemConstant.PARENT_DEPT_CANNOT_BE_SELF));
        SysDept rawDept = validateExistence(id);
        SysDept dept = baseConvert.toEntity(id, dto);
        validateUniqueness(dept);
        if (dto.getParentId() == null) {
            dept.setParentId(Func.TREE_ROOT_ID);
        }
        boolean result = updateById(dept);
        if (result && !rawDept.getParentId().equals(dept.getParentId())) {
            String taskName = StrUtil.format("部门移动事件:{}", dept.getDeptName());
            TaskUtil.createTask(SystemConstant.TASK_DEPT_UPDATE, taskName, 1, id);
        }
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void processDeptAddEvent(Long id) {
        SysDept dept = baseMapper.selectById(id);
        if (dept != null) {
            Integer parentRightValue;
            if (Func.TREE_ROOT_ID.equals(dept.getParentId())) {
                // 当新增的是顶级部门时,需要获取同级部门中最大的右值+1
                parentRightValue = getRootLevelMaxRightValue();
            } else {
                SysDept parentDept = baseMapper.selectById(dept.getParentId());
                parentRightValue = parentDept.getRightValue();
            }
            // 更新现有节点的左值和右值
            // 所有左值大于 parentRightValue 的节点，左值加2
            baseMapper.incrementLeftValue(parentRightValue, 2);
            // 所有右值大于等于 parentRightValue 的节点，右值加2
            baseMapper.incrementRightValue(parentRightValue, 2);
            // 更新新增节点的左值和右值
            dept.setLeftValue(parentRightValue).setRightValue(parentRightValue + 1);
            updateById(dept);
        }
    }

    private Integer getRootLevelMaxRightValue() {
        // 获取根节点最大右值
        Optional<SysDept> opt = getOneOpt(lambdaQuery().getWrapper().orderByDesc(SysDept::getRightValue).last("limit 1"));
        return opt.map(sysDept -> sysDept.getRightValue() + 1).orElse(1);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void processDeptUpdateEvent(Long id) {
        SysDept dept = baseMapper.selectById(id);
        if (dept != null) {
            // 获取目标父节点
            SysDept newParentDept = getParentDept(dept.getParentId());
            // 计算节点的宽度
            int width = dept.getRightValue() - dept.getLeftValue() + 1;
            // 查询所有需要移动的节点
            List<Long> moveIds = baseMapper.selectIdByRange(dept.getLeftValue(), dept.getRightValue());
            // 移动节点和目标父级节点的宽度
            int moveWidth;
            // 判断移动方向
            if (newParentDept.getRightValue() > dept.getRightValue()) {
                // 向右移
                moveWidth = newParentDept.getRightValue() - dept.getRightValue() - 1;
                baseMapper.decrementLeftValueByRange(dept.getRightValue(), newParentDept.getRightValue(), width);
                baseMapper.decrementRightValueByRange(dept.getRightValue(), newParentDept.getRightValue(), width);
            } else {
                // 向左移
                moveWidth = newParentDept.getLeftValue() - dept.getLeftValue() + 1;
                baseMapper.incrementLeftValueByRange(newParentDept.getLeftValue(), dept.getLeftValue(), width);
                baseMapper.incrementRightValueByRange(newParentDept.getLeftValue(), dept.getLeftValue(), width);
            }
            // 处理移动节点
            moveIds.add(id);
            // 计算层级差
            Integer levelDiff = dept.getDeptLevel() - newParentDept.getDeptLevel() - 1;
            // 更新移动节点的左右值和层级
            baseMapper.updateValueAndLevelBy(moveIds, moveWidth, levelDiff);
        }

    }

    private SysDept getParentDept(Long parentId) {
        SysDept dept;
        if (Func.TREE_ROOT_ID.equals(parentId)) {
            Integer rightValue = baseMapper.selectMaxRightValue();
            dept = new SysDept()
                    .setLeftValue(-1)
                    .setRightValue(rightValue + 1);
            dept.setId(Func.TREE_ROOT_ID);
        } else {
            dept = baseMapper.selectById(parentId);
        }
        return dept;
    }

    @Override
    public List<SysDeptVO> list(SysDeptListDTO dto) {
        LambdaQueryWrapper<SysDept> wrapper = lambdaQuery().getWrapper()
                .eq(dto.getDeptStatus() != null, SysDept::getDeptStatus, dto.getDeptStatus());
        if (Boolean.TRUE.equals(dto.getAllChildren())) {
            SysDept parentDept = validateExistence(dto.getParentId());
            wrapper.gt(SysDept::getLeftValue, parentDept.getLeftValue())
                    .lt(SysDept::getLeftValue, parentDept.getRightValue());
        } else {
            wrapper.eq(SysDept::getParentId, dto.getParentId());
        }
        List<SysDept> list = baseMapper.selectList(wrapper);
        return CollUtil.map(list, baseConvert::toVO, true);
    }


    @Override
    protected SysDept validateExistence(Long id) {
        SysDept dept = baseMapper.selectById(id);
        Assert.notNull(dept, () -> BizException.of(SystemConstant.DEPT_NOT_EXIST));
        return dept;
    }

    @Override
    protected void validateUniqueness(SysDept entity) {
        boolean existed = baseMapper.exists(lambdaQuery().getWrapper()
                .eq(SysDept::getDeptCode, entity.getDeptCode())
                .ne(entity.getId() != null, SysDept::getId, entity.getId())
        );
        Assert.isFalse(existed, () -> BizException.of(SystemConstant.DEPT_EXIST));
    }
}
